home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Copyright _ 2005, Apple Computer, Inc. All rights reserved.
- NOTE: Use of this source code is subject to the terms of the Software
- License Agreement for Mac OS X, which accompanies the code. Your use
- of this source code signifies your agreement to such license terms and
- conditions. Except as expressly granted in the Software License Agreement
- for Mac OS X, no other copyright, patent, or other intellectual property
- license or right is granted, either expressly or by implication, by Apple.
-
- */
-
-
- /*
- ****************************************
- * Objects used by the Scroller *
- ****************************************
- */
- var currentContent; // currently-visible DIV. Necessary for the Scroller to be shared across DIVs
- var currentContentStyle; // style object of the currentContent
- var scrollBar; // Parent scrollbar DIV. Contains the track and thumb
- var scrollThumb; // Scroller's thumb control
- var scrollTrack; // Scroller's base/track
- var trackTimer; // for extended mousedowns in the scroll track
-
- /*
- ********************************************
- * Dimensions used by the Scroller *
- ********************************************
- */
- var currentContentHeight; // height of currently-visible content DIV
- var currentContentTop; // top....
- var viewHeight; // height of the parent (overflow:hidden) view
- var trackMouseY; // mouse location in the scroll track
- var thumbHeight; // height of the thumb control
- var tracking = true; // is the mouse down in the scroll track
- var thumbStartY = -1; // point where we started scrolling with the thumb
- var scrollThumbStartPos = -1; // thumb's 'top' value when we started scrolling
- var scrollBarHeight; // height of our scrollbar. TBD
- var numberOfScrollablePixels; // for calculating thumb size/position and content position ("page number")
-
- /*
- ********************************************************
- * Constants. Hardcoded to match respective CSS values *
- ********************************************************
- */
- var SCROLLBAR_TOP = -1;
- var SCROLL_THUMB_HEIGHT = 27;
-
- // CSS element names of critical DIVs. Abstracted for easy customization.
- var SCROLLBAR_DIV_NAME = 'myScrollBar'; // Name of the parent scroller div, containing the track and the thumb.
- var SCROLLTHUMB_DIV_NAME = 'myScrollThumb'; // Div containing the scroller thumb
- var SCROLLTRACK_DIV_NAME = 'myScrollTrack'; // Div containing the scroller track
- var TRACK_TOP_DIV_NAME = 'myScrollTrackTop'; // Top edge of scroller track
- var TRACK_MID_DIV_NAME = 'myScrollTrackMid'; // variable-size center of scroller track
- var TRACK_BOT_DIV_NAME = 'myScrollTrackBot'; // Bottom edge of scroller track
-
- var PAGE_SKIP_PAUSE = 150; // time (ms) between page jumps when holding the mouse down in the track.
-
- // Calculate the height of the views and make the thumb proportional.
- // If a single scroller is being shared across multiple divs (as in this sample),
- // this function must be called whenever the divs swap, to recalibrate the scrollbar.
- function calculateAndShowThumb(contentDiv, resetTop) {
-
-
- scrollBar = document.getElementById(SCROLLBAR_DIV_NAME);
- scrollThumb = document.getElementById(SCROLLTHUMB_DIV_NAME);
- scrollTrack = document.getElementById(SCROLLTRACK_DIV_NAME);
-
-
-
- if (contentDiv != null) {
- currentContent = contentDiv;
- } else if (currentContent == null) {
- hideScrollbar();
- }
-
- // currentContent.style.display = 'block';
- currentContentStyle = document.defaultView.getComputedStyle(currentContent,'');
- DEBUG("cast: cct=" + currentContentStyle.getPropertyValue('top'));
- currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
- currentContentHeight = parseInt(currentContentStyle.getPropertyValue('height'));
-
- viewHeight = parseFloat (document.defaultView.getComputedStyle(currentContent.parentNode, '').getPropertyValue('height'));
- DEBUG("cast: viewHeight=" + viewHeight);
- scrollBarHeight = parseInt(document.defaultView.getComputedStyle(scrollBar, '').getPropertyValue('height'));
- DEBUG("cast: currentContentHeight=" + currentContentHeight + " top=" + currentContentTop + " scrollBarHeight=" + scrollBarHeight);
-
- var percent = getProportion (viewHeight, currentContentHeight);
- DEBUG("cast: percent=" + percent);
-
- // hide the scrollbar if all the content is showing. Determined by the calculated scrollbar height and position.
- if (percent == 0) {
- DEBUG("cast: 0% thumbHeight. Hiding scrollbar");
- hideScrollBar();
- } else {
- // Position the thumb according to where the content is currently scrolled.
- // This is necessary for sharing the same scrollbar between multiple content
- // panes that will likely be at different scroll positions.
- thumbHeight = Math.max(Math.round(scrollBarHeight * percent), SCROLL_THUMB_HEIGHT);
- if ( resetTop == true ) {
- thumbTop = -1;
- }
- else {
- thumbTop = thumbPositionForPagePosition(currentContentTop);
- }
-
- scrollThumb.style.height = thumbHeight + 'px';
- scrollThumb.style.top = thumbTop;
-
- numberOfScrollablePixels = scrollBarHeight - thumbHeight - SCROLLBAR_TOP;
- DEBUG("cast: new thumb height=" + scrollThumb.style.height);
-
- // This is a safeguard so the content matches the new thumb position. Necessary for live-resizing to work.
- scrollContent(thumbTop);
-
- showScrollBar();
- }
- }
-
- // Hide the thumb and track, but keep the parent scrollbar DIV around to preserve formatting
- function hideScrollBar() {
- scrollTrack.style.display = 'none';
- scrollThumb.style.display = 'none';
- }
-
- function showScrollBar() {
- scrollTrack.style.display = 'block';
- scrollThumb.style.display = 'block';
- }
-
- /*
- ********************************
- * Thumb Scrolling Functions *
- ********************************
- */
-
- // This mouseDown is presumably the start of a thumb drag (scroll) action.
- function mouseDownScrollThumb (event) {
- // We add these listeners and remove them later; they're only useful while there is mouse activity
- // on the thumb. This is necessary because there is no mousedrag event in JavaScript.
- document.addEventListener("mousemove", mouseMoveScrollThumb, true);
- document.addEventListener("mouseup", mouseUpScrollThumb, true);
-
- thumbStartY = event.y;
-
- scrollThumbStartPos = parseInt(document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top'));
-
- DEBUG("mdThumbHeight:" + thumbHeight + " thumbStartY=" + thumbStartY);
- }
-
- // At this point we are dragging the scrollThumb. We know this because the mousemove listener is only installed
- // after a mousedown.
- function mouseMoveScrollThumb (event) {
- var deltaY = event.y - thumbStartY;
-
- var newPosition = scrollThumbStartPos + deltaY;
- DEBUG("mmst: event.y=" + event.y + " thumbStartY=" + thumbStartY + " thumbStart=" + scrollThumbStartPos);
- scrollContent(newPosition);
- }
-
- function scrollContent(newThumbPosition) {
- DEBUG("scrollContent: newPositionIn=" + newThumbPosition);
-
- // Correct if we're going to clip above the top or below the bottom
- if (newThumbPosition < SCROLLBAR_TOP) {
- DEBUG("sc: thumb too high (" + newThumbPosition + ")");
- newThumbPosition = SCROLLBAR_TOP;
- } else if ((newThumbPosition + thumbHeight) > scrollBarHeight) {
- DEBUG("sc: thumb too low (" + newThumbPosition + ")");
- newThumbPosition = scrollBarHeight - thumbHeight;
- }
-
- DEBUG("scrollContent: newPosition=" + newThumbPosition);
- scrollThumb.style.top = newThumbPosition + 'px';
-
- DEBUG("calculating delta");
- currentContentTop = pagePositionForThumbPosition(newThumbPosition);
- DEBUG("scrollContent: thumbTop=" + scrollThumb.style.top + " currentContentTop is " + currentContentTop);
- currentContent.style.top = currentContentTop + 'px';
- }
-
- function mouseUpScrollThumb (event) {
- DEBUG("must: eventY=" + event.y);
- // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
- document.removeEventListener("mousemove", mouseMoveScrollThumb, true);
- document.removeEventListener("mouseup", mouseUpScrollThumb, true);
-
- // reset the starting position
- thumbStartY = -1;
- }
-
- /*
- ********************************
- * Track Scrolling Functions *
- ********************************
- */
-
- function mouseDownTrack (event) {
- updateTrackMouseY(event);
-
- scrollTrack.addEventListener("mousemove", mouseMoveTrack, false);
- scrollTrack.addEventListener("mouseover", mouseOverTrack, false);
- scrollTrack.addEventListener("mouseout", mouseOutTrack, false);
-
- // This is our handling for clicks in the track.
- var thumbTop = document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top');
- DEBUG("trackClick: mouseY=" + trackMouseY + " scrollThumbY=" + thumbTop);
- if (trackMouseY > parseInt(thumbTop)) {
- DEBUG("click BELOW thumb ");
- pageDown();
- trackTimer = setInterval("pageDown();", PAGE_SKIP_PAUSE);
- } else {
- DEBUG("click ABOVE thumb");
- pageUp();
- trackTimer = setInterval("pageUp();", PAGE_SKIP_PAUSE);
- }
-
- DEBUG("mdt: newPosition=" + trackMouseY);
- }
-
- function mouseMoveTrack(event) {
- // If the mouse moved while being held down, update the location so we
- // stop the track-scrolling in the right place.
- updateTrackMouseY(event);
- }
-
- function mouseOutTrack(event) {
- // When the mouse moves out while pressed, we turn track-scrolling off.
- // The timer keeps firing, but pageUp/pageDown exits based on this value
- tracking = false;
- }
-
- function mouseOverTrack(event) {
- // The timer is still firing, but pageUp/pageDown are waiting for the mouse to
- // return to the track. This will resume track-scrolling.
- tracking = true;
- }
-
- function mouseUpTrack(event) {
- // stop track-scrolling
- clearInterval(trackTimer);
-
- // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
- scrollTrack.removeEventListener("mousemove", mouseMoveTrack, false);
- scrollTrack.removeEventListener("mouseover", mouseMoveTrack, false);
- scrollTrack.removeEventListener("mouseout", mouseMoveTrack, false);
- }
-
- // correct the coordinates for the sourceEvent so they properly match the source component
- // **YOU MAY NEED TO UPDATE THIS FUNCTION** depending on how deeply the scrollbar div is nested
- function updateTrackMouseY (event) {
- DEBUG("utmY: source=" + event.toElement.id + " offsetY=" + event.offsetY + " layerY=" + event.layerY + " offsetTop=" + event.toElement.offsetTop);
-
- if (event.toElement.id == 'myScrollTrackMid') {
- // source is the ctr component of the track; offset by the top component.
- var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
- trackMouseY = event.offsetY + parseInt(topHeight);
- DEBUG("utmY: click in mid, topHeight=" + topHeight);
- } else if (event.toElement.id == TRACK_BOT_DIV_NAME) {
- // source is the bottom component of the track; offset by the top and the middle.
- var midHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_MID_DIV_NAME)).getPropertyValue('height');
- var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
- trackMouseY = event.offsetY + parseInt(midHeight) + parseInt(topHeight);
- DEBUG("utmY: click in bottom, offsetY=" + event.offsetY + " offsetTop=" + event.toElement.offsetTop + " mid+top=" + (parseInt(midHeight) + parseInt(topHeight)));
- } else {
- DEBUG("utmY: click in top, offsetY=" + event.offsetY + " offsetTop=" + event.toElement.offsetTop + " parentTop=" + event.toElement.parentNode.offsetTop);
- // source is the top of the track
- trackMouseY = event.offsetY - (event.toElement.offsetTop + event.toElement.parentNode.offsetTop);
- }
-
- DEBUG("utmY: trackMouseY" + trackMouseY);
- }
-
- /*
- ********************************************************************
- * pageUp/pageDown *
- * Used by track-scrolling code, but can be called independently *
- ********************************************************************
- */
-
- // Reposition the content one page (viewHeight) upwards. Prevent out-of-bounds values.
- // Remember that the content top becomes increasingly NEGATIVE (moves upwards) as we scroll down.
- function pageDown() {
- if (!tracking) return;
- // calculate the last page. This is equal to the content's full height, less one viewHeight
- // Again, the value is negative because that's how far offset the content would need to be.
- currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
- var lastPageY = -(currentContentHeight - viewHeight);
- // calculate the next page from the content's current position.
- var nextPageY = currentContentTop - viewHeight;
- DEBUG("pageDown: currentContent.lastPageY=" + lastPageY + " nextPageY=" + nextPageY);
- currentContentTop = Math.max(lastPageY, nextPageY);
- currentContent.style.top = currentContentTop + 'px';
-
- // reposition the scroll thumb based on the new page position.
- var newThumbTop = thumbPositionForPagePosition(currentContentTop);
- var thumbBottom = newThumbTop + parseInt(scrollThumb.style.height);
- scrollThumb.style.top = newThumbTop;
-
- DEBUG("pageDown: currentContentTop=" + currentContentTop + " currentContentHeight=" + currentContentHeight + " newThumbTop=" + newThumbTop + " thumbBottom=" + thumbBottom);
-
- if (trackMouseY < thumbBottom) {
- // the thumb has met the mouse; time to stop track-scrolling.
- clearInterval(trackTimer);
- }
- }
-
- // very similar to pageDown, with some values negated to move the content in a different direction.
- function pageUp() {
- if (!tracking) return;
- currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
- var firstPageY = 0;
- var nextPageY = currentContentTop + viewHeight;
- currentContentTop = Math.min(firstPageY, nextPageY)
- currentContent.style.top = currentContentTop + 'px';
-
- var newThumbTop = thumbPositionForPagePosition(currentContentTop);
- DEBUG("pageUp: contentTop=" + currentContentTop + " newThumbTop=" + newThumbTop);// + " thumbBottom=" + thumbBottom);
- scrollThumb.style.top = newThumbTop;
-
- if (trackMouseY > newThumbTop) {
- clearInterval(trackTimer);
- }
- }
-
- /*
- ********************************
- * Utility / Math functions *
- ********************************
- */
-
- function getProportion (viewheight, documentheight) {
- if (documentheight <= viewheight)
- return 0;
- else
- return viewheight/documentheight;
- }
-
- // Given the position of the thumb, tell us what the content top should be.
- // This is the key value that allows us to thumb-scroll.
- function pagePositionForThumbPosition (thumbPosition) {
- return -(thumbPosition - SCROLLBAR_TOP) * ((currentContentHeight - viewHeight) / numberOfScrollablePixels);
- }
-
- // Given the position of the page, tell us where the thumb should be.
- // This is the key value that allows us to track-scroll.
- function thumbPositionForPagePosition (pagePosition) {
- return -(pagePosition / ((currentContentHeight - viewHeight) / numberOfScrollablePixels)) + SCROLLBAR_TOP;
- }
-
- /*
- ****************************
- * END SCROLLER FUNCTIONS *
- ****************************
- */
-
- // debug code uses the div defined in Scroller.html demo
-
- var debugMode = false;
-
- // write to the debug div.
- function DEBUG(str) {
- if (debugMode) {
- if (window.widget) {
- alert(str);
- } else {
- document.getElementById('debugDiv').innerHTML += str + "<br>";
- }
- }
- }
-
- // toggle the debugMode flag, but only show the debugDiv if we're in Safari
- function toggleDebug() {
- debugMode = !debugMode;
- if (debugMode == true && !window.widget) {
- document.getElementById('debugDiv').style.display = 'block';
- } else {
- document.getElementById('debugDiv').style.display = 'none';
- }
- }